package Statistics;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;

import Alpha.LocalAlphaCalculatorClass;
import Definitions.GraphClass;
import Global.ConstantVariable;
import Global.DirectoryStructureAndSettingsClass;
import Global.GlobalClass;
import GraphProperties.ShortestPathRelatedPropsClass;
import Integrity.IntegrityCheckerClass;
import ShortestPath.ConnectedComponentsAnalyzerClass;
import ShortestPath.ConnectedComponentsAnalyzerInParallelClass;
import SyntheticData.syntheticDataGeneratorClass;
import Utility.FileUtilityClass;
import Utility.WorkerUtilityClass;


public class statisticsManagerClass {
	
	GraphClass currentGraph;
	GlobalClass global;
	contentInfoExtractorClass contentInfoExtractor;
	DegreeStatistics degreeStatistics;
	clusterCoefficients ClusterCoefficients;	
	homophilyCalculatorClass homophilyCalculator;
	ConnectedComponentsAnalyzerClass ConnectedComponentsAnalyzer;
	ConnectedComponentsAnalyzerInParallelClass ConnectedComponentsAnalyzerInParallel;
	metricsCalculatorInParallelClass metricsCalculatorInParallel;
	LocalAlphaCalculatorClass LocalAlphaCalculatorInParallel; 
	int edgeType;
	
	String datasetName;
	
	public statisticsManagerClass(GraphClass g, GlobalClass global, int edgeType, String datasetName)
	{
		currentGraph = g;
		this.global = global;		
		
		this.edgeType = edgeType;		
		//currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().add(new PropertiesOfGraphClass(currentGraph, edgeType, datasetName));
		contentInfoExtractor = new contentInfoExtractorClass(currentGraph, global);
		degreeStatistics=new DegreeStatistics(currentGraph, edgeType);
		ClusterCoefficients = new clusterCoefficients(currentGraph, edgeType, degreeStatistics);
		homophilyCalculator = new homophilyCalculatorClass(currentGraph, edgeType, degreeStatistics);
		//ConnectedComponentsAnalyzer = new ConnectedComponentsAnalyzerClass(currentGraph, edgeType);
		//ConnectedComponentsAnalyzerInParallel = new ConnectedComponentsAnalyzerInParallelClass(currentGraph, edgeType);
		metricsCalculatorInParallel = new metricsCalculatorInParallelClass(currentGraph, edgeType);
		LocalAlphaCalculatorInParallel = new LocalAlphaCalculatorClass(currentGraph, global);
	}
	
	public void execute()
	{	
		System.out.println("Starting calculating statistics for : "+ edgeType);
		
		contentInfoExtractor.printInfoAboutContent();		
		
		degreeStatistics.calculateDegreeStatistics();
		
		degreeStatistics.calculateRichClubCoefficientsWrtDegreeLevels();
		
		System.out.println("Before:calculateAllShortestPaths");
    	long processStartTime = System.currentTimeMillis();
    	if(IntegrityCheckerClass.checkIfItIsNecessaryToRecalculateShortestPaths(edgeType)==true)
    	{    		
    		metricsCalculatorInParallel.calculateAllShortestPaths();
    		FileUtilityClass.writeObjectToFile(currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType).shortestPathRelatedProps, IntegrityCheckerClass.getFileNameForShortestPathObject(edgeType));    		
    	}
    	else
    	{
    		currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType).shortestPathRelatedProps = (ShortestPathRelatedPropsClass)FileUtilityClass.readObjectFromFile(IntegrityCheckerClass.getFileNameForShortestPathObject(edgeType));
    		currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType).shortestPathRelatedProps.setPropertiesIBelongTo(currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType));    		
    	}
    	
    	System.out.println("Diameter:"+currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType).shortestPathRelatedProps.getDiameter());
    	System.out.println("Avg Path Length:"+currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType).shortestPathRelatedProps.getAveragePathLength());
    	System.out.println("Total consumed time(Djikstra):"+(System.currentTimeMillis()-processStartTime)/1000);
    	System.out.println("After:calculateAllShortestPaths");
    	
    	double diameter = currentGraph.getPropertiesOfTheGraphWithRespectToEdgeType().get(edgeType).shortestPathRelatedProps.getDiameter();
    	double[] hopValues = new double[(int)diameter+1];
    	double[] homophilyVersusHops = new double[(int)diameter+1];
    	
    	if(diameter==0)
    	{
    		homophilyCalculator.calculateHomophilyOfTheGraph(1);
    	}
    	else
    	{
    		for(int i=1; i<=diameter; i++)
    		{	
    			hopValues[i-1]=i;
    			homophilyVersusHops[i-1] = homophilyCalculator.calculateHomophilyOfTheGraph(i);
    			homophilyCalculator.print();
    		}
    	}
    	
    	printHomophilyResultsToFile(hopValues, "Hops", edgeType);
    	printHomophilyResultsToFile(homophilyVersusHops, "HomophilyVsHops", edgeType);    	
		
		degreeStatistics.print();
		 	  
  	  	global.linksChecker.printLinkFileInfo();
  	  	printEdgesFileInfo();  	  	
  	  	
    	ClusterCoefficients.calculateClusterCoefficients(currentGraph);
    	ClusterCoefficients.print(); 
    	
    	//ConnectedComponentsAnalyzer.calculateAllConnectedComponents();
    	//int sp=ConnectedComponentsAnalyzer.findShortestPathBetween(currentGraph.getDataSet().get(1), currentGraph.getDataSet().get(3));
    	//System.out.println("sp:"+sp);
    	
    	//MetricsCalculatorClass metricsCalculator = new MetricsCalculatorClass(currentGraph);
    	
    	if(edgeType == ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH)
    	{
    		if(ConstantVariable.ExecutionParameters.calculateGraphHopsGraphs)
    			LocalAlphaCalculatorInParallel.TestWithWekaInParallel(currentGraph, global, ConstantVariable.ExecutionParameters.NUMBER_OF_AT_MOST_HOPS, null);
    		
    		if(ConstantVariable.ExecutionParameters.isLocalAlphaCalculationForTheGraphEnabled)
    			LocalAlphaCalculatorInParallel.calculateLocalAlpha(ConstantVariable.AlphaRelated_ConstantVariables.DEFAULT_NEIGHBOURHOOD_LEVEL_FOR_LINK_BASED_LINKS, ConstantVariable.AlphaRelated_ConstantVariables.DEFAULT_NEIGHBOURHOOD_LEVEL_FOR_CONTENT_BASED_LINKS);
    	}
    	
    	/*
    	System.out.println("calculateAllConnectedComponents Start");
    	processStartTime = System.currentTimeMillis();
    	ConnectedComponentsAnalyzerInParallel.calculateAllConnectedComponents(Common_ConstantVariables.SYMMETRIC);
    	System.out.println("Total consumed time(Djikstra):"+(System.currentTimeMillis()-processStartTime)/1000);
    	//ConnectedComponentsAnalyzer.calculateAllConnectedComponents();
    	System.out.println("calculateAllConnectedComponents End");
    	//int sp=ConnectedComponentsAnalyzerInParallel.findShortestPathBetween(currentGraph.getDataSet().get(1), currentGraph.getDataSet().get(3));
    	//System.out.println("sp:"+sp);
    	 * 
    	 */
    	
//  		//System.out.println("ciftYonluKomsuSayisi:"+Node.ciftYonluKomsuSayisi+" xtekYonluKomsuSayisi:"+Node.xtekYonluKomsuSayisi+" ytekYonluKomsuSayisi:"+Node.ytekYonluKomsuSayisi);
    	
    	System.out.println("Ending calculating statistics for : "+ edgeType);
    	
    	  	  	
	}
	
	public void printEdgesFileInfo()
	{		
		WorkerUtilityClass.printSeperator();
		System.out.println("# of Links in the edges file: " + global.linksChecker.getNumberOfLinksInTheFile());
		System.out.println("# of Parallel Links on the Graph:" +global.linksChecker.getParalelLinkSayisi());
		System.out.println("# of Self Citations in the edges file: "+global.linksChecker.getNumberOfSelfCitations());
		System.out.println("# of Edges for Non-Existing Nodes: "+global.linksChecker.getNumberOfLinksForNonExistingNodes());
		
		WorkerUtilityClass.printShorterSeperator();
		System.out.println("# of Links without self citations: "+ (global.linksChecker.getNumberOfLinksInTheFile()- global.linksChecker.getNumberOfSelfCitations()));		
		System.out.println("# of Links without SC & Links Non-Exiting Nodes: "+ (global.linksChecker.getNumberOfLinksInTheFile()- global.linksChecker.getNumberOfSelfCitations()-global.linksChecker.getNumberOfLinksForNonExistingNodes()));
		System.out.println("# of Clean Links: "+ (global.linksChecker.getNumberOfLinksInTheFile()- global.linksChecker.getParalelLinkSayisi() - global.linksChecker.getNumberOfSelfCitations()-global.linksChecker.getNumberOfLinksForNonExistingNodes()));
		WorkerUtilityClass.printSeperator();
	}	
	
	DegreeStatistics getDegreeStatistics()
	{
		return degreeStatistics;		
	}
	
	void printHomophilyResultsToFile(double[] array, String nameOfTheArray, int edgeType)
	{
		try {
			BufferedWriter out = createRawHomophilyVersusHopsResultsFile(DirectoryStructureAndSettingsClass.getDatasetName(), edgeType);

			out.write(nameOfTheArray+"=[");

			for(int i=0; i< array.length ;i++)
			{
				//if(degreeLevelsArrayList.get(i).getDegreeLevel()!=0)
				{
					out.write(Double.toString(array[i]));
					if(i!=array.length-1)
						out.write(",");
				}
			}
			out.write("]");
			
			out.write("\n");
			out.close();
			
		} catch (IOException e) {
		e.printStackTrace();
		}
	}
	
	 public BufferedWriter createRawHomophilyVersusHopsResultsFile(String datasetName, int edgeType)
	 {
		 String path= ConstantVariable.DefaultDirectoryNamesConstants.ROOT_DIRECTORY
		 +ConstantVariable.DefaultDirectoryNamesConstants.DirectoriesUnderRootDirectory.LATEST_RESULTS_DIRECTORY;
		 
		 if(GlobalClass.RunningValues.executionFor!=ConstantVariable.DataSet_Constants.SYNTETIC_DATASET)
			 path+=ConstantVariable.DefaultDirectoryNamesConstants.DirectoriesUnderLatestResultsDirectory.PREDIFINED_DATASETS_RESULTS_DIRECTORY;
		 else
			 path+=ConstantVariable.DefaultDirectoryNamesConstants.DirectoriesUnderLatestResultsDirectory.SYNTETIC_DATASETS_RESULTS_DIRECTORY;

		 String fileName = datasetName+ConstantVariable.Format_ConstantVariables.DASH_CHAR+edgeType+ConstantVariable.Format_ConstantVariables.DASH_CHAR+"HomophilyVersusHops.txt"; 
		 try {

			 File descFile=DirectoryStructureAndSettingsClass.createFile(path, fileName);
			 FileWriter fstream;
			 fstream = new FileWriter(descFile, true);
			 BufferedWriter out = new BufferedWriter(fstream);
			 return out;
		 } catch (IOException e) {
			 // TODO Auto-generated catch block
			 e.printStackTrace();
		 }
		 return null;
	 }
	 
	 public static BufferedWriter createGraphPropsSummaryResultsFile()
	 {
		 String path = DirectoryStructureAndSettingsClass.getFullPathOfTheRootDirectoryNameForTheCurrentDataset()
		 +ConstantVariable.DefaultDirectoryNamesConstants.DirectoriesUnderDatasetDirectory.GRAPH_PROPERTIES_FOLDER_NAME
		 +ConstantVariable.DefaultDirectoryNamesConstants.DirectoriesUnderGraphPropertiesDirectory.SUMMARY_RELATED_DATA_FOLDER_NAME;

		 //System.out.println("RootDirName:"+DirectoryStructureAndSettingsClass.getFullPathOfTheRootDirectoryNameForTheCurrentDataset());

		 try {
			 File descFile=DirectoryStructureAndSettingsClass.createFile(path, DirectoryStructureAndSettingsClass.getDatasetName()+ ConstantVariable.SimilarityCalculator_ConstantVariables.DEFAULT_SIMILARITY_MEASURE+ConstantVariable.DefaultFileNamesConstants.GRAPH_PROPS_SUMMARY_RESULTS_FILE_SUFFIX);
			 FileWriter fstream;
			 fstream = new FileWriter(descFile);
			 BufferedWriter out = new BufferedWriter(fstream);
			 
			 return out;
		 } catch (IOException e) {
			 // TODO Auto-generated catch block
			 e.printStackTrace();
		 }
		 return null;
		 
	 }
	 
	 static void fillTheSummaryGraphPropsFile(BufferedWriter out, GraphClass graph, GlobalClass global)
	 {
		 NumberFormat formatter;
		 			
		 formatter = new DecimalFormat(ConstantVariable.Format_ConstantVariables.numberFormatToBeUsedForPrecisionLevelOfContentSimilarity);
		 formatter.setMinimumFractionDigits(2);
		 formatter.setMaximumFractionDigits(4);
			
		 try {
			 out.write("Dataset Size\t\t");
			 out.write(Integer.toString(graph.getDataSet().size()));			
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 
			 out.write("# of Content Features\t\t");
			 out.write(Integer.toString(graph.getDataSet().get(0).getContentFeatures(ConstantVariable.Common_ConstantVariables.DEFAULT_CONTENT_TYPE).length));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 if(GlobalClass.RunningValues.executionFor==ConstantVariable.DataSet_Constants.SYNTETIC_DATASET)
			 {
				 out.write("# of Link Features\t\t");
				 out.write(Integer.toString(ConstantVariable.SyntheticDataGeneration_Constants.SyntheticDataGenerationParameter_Constants.NUMBER_OF_LINK_BITS));
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("# of Shared Features\t\t");
				 out.write(Integer.toString(syntheticDataGeneratorClass.getNumberOfSharedBits()));
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("Similarity Threshold\t\t");
				 out.write(formatter.format((double)ConstantVariable.SyntheticDataGeneration_Constants.SyntheticDataGenerationParameter_Constants.THRESHOLD/ConstantVariable.SyntheticDataGeneration_Constants.SyntheticDataGenerationParameter_Constants.NUMBER_OF_LINK_BITS));
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("Biased Probability\t\t");
				 out.write(formatter.format(ConstantVariable.SyntheticDataGeneration_Constants.SyntheticDataGenerationParameter_Constants.BIASED_PROBABILITY));
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 }
			 else
			 {
				 out.write("# of Link Features\t\t");
				 out.write(ConstantVariable.Format_ConstantVariables.DASH_CHAR);
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("# of Shared Features\t\t");
				 out.write(ConstantVariable.Format_ConstantVariables.DASH_CHAR);
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("Biased Probability\t\t");
				 out.write(ConstantVariable.Format_ConstantVariables.DASH_CHAR);
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("Similarity Threshold\t\t");
				 out.write(ConstantVariable.Format_ConstantVariables.DASH_CHAR);
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 
				 out.write("Biased Probability\t\t");
				 out.write(ConstantVariable.Format_ConstantVariables.DASH_CHAR);
				 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 }
			 
			 out.write("# of Classes\t\t");
			 out.write(Integer.toString(global.classSize));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("# of Links(Content)\t\t");
			 out.write(Integer.toString((int)Math.ceil(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).degreeDistributionRelatedProps.getAverageDegreeOfNodes()*graph.getDataSet().size()/2)));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("# of Links(Link)\t\t");
			 out.write(Integer.toString((int)Math.floor(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).degreeDistributionRelatedProps.getAverageDegreeOfNodes()*graph.getDataSet().size()/2)));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("# of Classes\t\t");
			 out.write(Integer.toString(global.classSize));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 for(int i=0; i<global.classSize; i++)
			 {
				 if(global.frequencyOfEachClass!=null)
				 {
					 out.write("Class "+i+":"+ global.classList.get(i).getName()+" ("+(global.frequencyOfEachClass[i])+" )"+"\t\t");				 
					 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
				 }
			 }
			 			 
			 out.write("Average Degree(Content)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).degreeDistributionRelatedProps.getAverageDegreeOfNodes()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Average Degree(Link)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).degreeDistributionRelatedProps.getAverageDegreeOfNodes()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Homophily(Content)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).homophilyRelatedProps.getHomophilyOfTheGraph()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Homophily(Link)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).homophilyRelatedProps.getHomophilyOfTheGraph()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Clustering Coefficient(Content)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).clusteringCoefficientRelatedProps.getClusteringCoefficientOfTheGraph()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Clustering Coefficient(Link)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).clusteringCoefficientRelatedProps.getClusteringCoefficientOfTheGraph()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Diameter(Content)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).shortestPathRelatedProps.getDiameter()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 			 
			 out.write("Diameter(Link)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).shortestPathRelatedProps.getDiameter()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Density(Content)\t\t");
			 out.write(formatter.format((graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).degreeDistributionRelatedProps.getAverageDegreeOfNodes()*graph.getDataSet().size())/(graph.getDataSet().size()*(graph.getDataSet().size()-1))));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Density(Link)\t\t");
			 out.write(formatter.format((graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).degreeDistributionRelatedProps.getAverageDegreeOfNodes()*graph.getDataSet().size())/(graph.getDataSet().size()*(graph.getDataSet().size()-1))));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 
			 out.write("Average Path Length(Content)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE_FOR_CONTENT_GRAPH).shortestPathRelatedProps.getAveragePathLength()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 			 
			 out.write("Average Path Length(Link)\t\t");
			 out.write(formatter.format(graph.getPropertiesOfTheGraphWithRespectToEdgeType().get(ConstantVariable.Common_ConstantVariables.DEFAULT_EDGE_TYPE).shortestPathRelatedProps.getAveragePathLength()));
			 out.write(ConstantVariable.Format_ConstantVariables.LINE_END);
			 		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 
	 }
	 
	 public static void tryToPrepareGraphPropsSummary(GraphClass graph, GlobalClass global)
	 {
		 try {
			 BufferedWriter outForGraphPropsSummaryFile;
			 outForGraphPropsSummaryFile = createGraphPropsSummaryResultsFile();
			 fillTheSummaryGraphPropsFile(outForGraphPropsSummaryFile, graph, global);
			 outForGraphPropsSummaryFile.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		 
	 }
}
